package com.yc.cloud.common.websocket.core;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.StrUtil;
import com.corundumstudio.socketio.SocketIOClient;
import com.corundumstudio.socketio.SocketIONamespace;
import com.corundumstudio.socketio.SocketIOServer;
import com.yc.cloud.common.websocket.dto.MessageUserDisConnectedDto;
import com.yc.cloud.common.websocket.dto.RoomMessageDto;
import com.yc.cloud.common.websocket.dto.SingleMessageDto;
import com.yc.cloud.common.websocket.listener.PlayerStatusListener;
import com.yc.cloud.common.websocket.runnable.ConnectedMessageRunnable;
import com.yc.cloud.common.websocket.service.SocketService;
import com.yc.cloud.common.websocket.vo.PlayerVo;
import jakarta.annotation.PreDestroy;
import lombok.Setter;
import lombok.extern.slf4j.Slf4j;
import lombok.val;
import org.springframework.stereotype.Component;

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

/**
 * WebSocket 核心实现类,负责启动、初始化、事件监听等功能
 */
@Component
@Slf4j
public class SocketServer implements SocketService {

    private final SocketIOServer socketIOServer;
    private List<String> namespaceList;
    @Setter
    private String namespace = "/";
    private List<SocketIONamespace> socketIONamespaceList;
    public static Map<String, PlayerVo> clientMap = new ConcurrentHashMap<>();
    //改造下
    private final ExecutorService executor = Executors.newFixedThreadPool(3);
    private final List<PlayerStatusListener> playerStatusListeners = new ArrayList<>();


    public SocketServer(SocketIOServer socketIOServer) {
        this.socketIOServer = socketIOServer;
    }


    /**
     * 启动socketServer
     */
    public void start() {
        initNameSpace();
        addListener();
        socketIOServer.start();
    }

    @PreDestroy
    private void stop() {
        clientMap.clear();
        clientMap = null;
        if (socketIOServer != null) {
            socketIOServer.stop();
        }
    }

    /**
     * 初始化命令空间对象集合
     */
    private void initNameSpace() {
        namespaceList = Collections.singletonList(namespace);
        socketIONamespaceList = new ArrayList<>(namespaceList.size());
        for (String namespace : namespaceList) {
            final SocketIONamespace socketIONamespace = socketIOServer.addNamespace(namespace);
            socketIONamespaceList.add(socketIONamespace);
        }
    }

    /**
     * 添加事件监听,包含用户连接、断开等操作
     */
    private void addListener() {
        addConnectListener();
        addDisconnectListener();
        // 监听客户端连接
        SocketMessageManager socketMessageProcessor = new SocketMessageManager(socketIONamespaceList, this);
        socketMessageProcessor.addAllListener();
    }

    public void addConnectListener() {
        for (SocketIONamespace socketIONamespace : socketIONamespaceList) {
            socketIONamespace.addConnectListener(client -> {
                var id = getSocketId(client);
                if (StrUtil.isNotEmpty(id)) {
                    val playVo = SocketClientInfoManager.getInstance().convertPlayerVo(client);
                    //通知注册玩家上下线状态监听器
                    notifyPlayerStatusListeners(playVo, true);
                    val displayName = playVo.getDisplay();
                    //判断用户是否重复登录
                    val onLinePlayer = getOnLinePlayer(displayName);
                    if (onLinePlayer != null) {
                        forceDisconnect(onLinePlayer.getId());
                    }
                    //添加新的用户
                    clientMap.put(id, playVo);
                }
                String display = SocketClientInfoManager.getInstance().getDisplayParamsByClient(client);
                var msg = "用户:" + display + ",id=" + id + " 上线,在线用户总数量:" + clientMap.size();
                log.info(msg);

                val room = SocketClientInfoManager.getInstance().getRoomParamsByClient(client);
                //让他加入该房间
                if (StrUtil.isNotEmpty(room)) {
                    client.joinRoom(room);
                }
                executor.submit(new ConnectedMessageRunnable(this, client));
                //  broadcastRoomMessageToUser(socketIONamespace, room, SocketConstants.MESSAGE_GET_ONLINE_PLAYER_COUNT_RESULT, getOnLinePlayerStatics(client));
                executor.submit(new SocketMessageProcessor(this, SocketMessage.USER_CONNECTED, client));
            });
        }
    }


    public void addDisconnectListener() {
        for (SocketIONamespace socketIONamespace : socketIONamespaceList) {
            socketIONamespace.addDisconnectListener(client -> {
                String display = SocketClientInfoManager.getInstance().getDisplayParamsByClient(client);
                if (StrUtil.isNotEmpty(display)) {
                    val playVo = SocketClientInfoManager.getInstance().convertPlayerVo(client);
                    notifyPlayerStatusListeners(playVo, false);
                    val room = SocketClientInfoManager.getInstance().getRoomParamsByClient(client);
                    if (StrUtil.isNotEmpty(room)) {
                        client.leaveRoom(room);
                    }
                    if (CollUtil.isNotEmpty(clientMap)) {
                        clientMap.remove(SocketClientInfoManager.getInstance().getSocketId(client));
                    }
                    val id = getSocketId(client);
                    var msg = "用户:" + display + ",id=" + id + " 下线,在线用户总数量:" + clientMap.size();
                    log.info(msg);
                    val message = new MessageUserDisConnectedDto(id, display);
                    broadcastRoomMessageToUser(socketIONamespace, room, SocketConstants.MESSAGE_USER_DISCONNECTED, message);
                    client.disconnect();
                    //通知前端玩家数量有变化

                    // broadcastRoomMessageToUser(socketIONamespace, room, SocketConstants.MESSAGE_GET_ONLINE_PLAYER_COUNT_RESULT, getOnLinePlayerStatics(client));
                }
            });
        }
    }

    public String getSocketId(SocketIOClient client) {
        var id = client.getSessionId().toString();
        val socketIONamespace = client.getNamespace();
        if (socketIONamespace != null && StrUtil.isNotEmpty(socketIONamespace.getName())) {
            id = socketIONamespace.getName() + "#" + id;
        }
        return id;
    }

    public SocketIOClient getSocketIOClient(String currentDisPlay) {
        if (StrUtil.isEmpty(currentDisPlay)) {
            return null;
        }
        for (SocketIONamespace namespace : socketIONamespaceList) {
            val clients = namespace.getAllClients();
            for (SocketIOClient socketIOClient : clients) {
                val display = SocketClientInfoManager.getInstance().getDisplayParamsByClient(socketIOClient);
                if (currentDisPlay.equals(display)) {
                    return socketIOClient;
                }
            }
        }
        return null;
    }

    public SocketIOClient getSocketIOClientBySocketId(String currentSocketId) {
        if (StrUtil.isEmpty(currentSocketId)) {
            return null;
        }
        for (SocketIONamespace namespace : socketIONamespaceList) {
            val clients = namespace.getAllClients();
            for (SocketIOClient socketIOClient : clients) {
                val socketId = getSocketId(socketIOClient);
                // val display = SocketClientInfoManager.getInstance().getDisplayParamsByClient(socketIOClient);
                if (currentSocketId.equals(socketId)) {
                    return socketIOClient;
                }
            }
        }
        return null;
    }

    public PlayerVo getOnLinePlayer(String currentDisPlay) {
        if (StrUtil.isEmpty(currentDisPlay)) {
            return null;
        }
        for (PlayerVo playerVo : clientMap.values()) {
            if (currentDisPlay.equals(playerVo.getDisplay())) {
                return playerVo;
            }
        }
        return null;
    }


    @Override
    public List<PlayerVo> getOnLineUsers() {
        val playerList = new ArrayList<PlayerVo>();
        clientMap.forEach((s, playerVo) -> {
            playerList.add(playerVo);
        });
        return playerList;
    }

    /**
     * 强制下线
     *
     * @param socketId 客户端ID
     */
    public void forceDisconnect(String socketId) {
        if (StrUtil.isEmpty(socketId)) {
            return;
        }
        log.info("强制下线用户id {}", socketId);
        val socketIOClient = getSocketIOClientBySocketId(socketId);
        if (socketIOClient != null) {
            log.info("强制下线用户 {}", SocketClientInfoManager.getInstance().getDisplayParamsByClient(socketIOClient));
            socketIOClient.disconnect();
        }
        if (clientMap != null && clientMap.containsKey(socketId)) {
            clientMap.remove(socketId);
        }
    }


    @Override
    public void broadcastAllOnlineUsers(SocketIOClient socketIOClient, String event, Object... message) {
        socketIOClient.getNamespace().getBroadcastOperations().sendEvent(event, message);
    }

    @Override
    public SendMessageResult<String> sendSingleMessage(SingleMessageDto messageDto) {
        val socketClient = getSocketIOClient(messageDto.getReceiver());
        if (socketClient == null) {
            return SendMessageResult.failed(SocketErrorConstants.USER_NOT_ONLINE);
        }
        socketClient.sendEvent(SocketMessage.RECEIVE_SINGLE_MESSAGE, messageDto);
        return SendMessageResult.success(SocketConstants.SEND_MESSAGE_SUCCESS);
    }

    @Override
    public SendMessageResult<String> sendRoomMessage(RoomMessageDto messageDto) {
        val socketClient = getSocketIOClient(messageDto.getSender());
        if (socketClient == null) {
            return SendMessageResult.failed(SocketErrorConstants.USER_NOT_ONLINE);
        }
        broadcastRoomMessageToUser(socketClient.getNamespace(), messageDto.getRoom(), SocketMessage.RECEIVE_ROOM_MESSAGE, messageDto);
        return SendMessageResult.success(SocketConstants.SEND_MESSAGE_SUCCESS);
    }

    @Override
    public void broadcastRoomMessageToUser(SocketIONamespace socketIONamespace, String room, String event, Object... message) {
        if (socketIONamespace == null) {
            if (StrUtil.isNotEmpty(room)) {
                socketIOServer.getRoomOperations(room).sendEvent(event, message);
            } else {
                socketIOServer.getBroadcastOperations().sendEvent(event, message);
            }
        } else {
            if (StrUtil.isNotEmpty(room)) {
                socketIONamespace.getRoomOperations(room).sendEvent(event, message);
            } else {
                socketIONamespace.getBroadcastOperations().sendEvent(event, message);
            }
        }
    }


    @Override
    public List<PlayerVo> listByRoomId(SocketIOClient client, String room) {
        val roomOperations = client.getNamespace().getRoomOperations(room);
        val playerList = new ArrayList<PlayerVo>();
        if (roomOperations == null) {
            return playerList;
        }
        val clients = roomOperations.getClients();
        for (SocketIOClient socketIOClient : clients) {
//            val player = SocketClientInfoUtils.convertPlayerVo(socketIOClient);
            val player = clientMap.getOrDefault(getSocketId(socketIOClient), null);
            if (player != null) {
                playerList.add(player);
            }
        }
        return playerList;
    }


    // 注册玩家状态监听器
    public void registerUserStatusListener(PlayerStatusListener listener) {
        playerStatusListeners.add(listener);
    }

    // 注销玩家状态监听器
    public void unregisterUserStatusListener(PlayerStatusListener listener) {
        playerStatusListeners.remove(listener);
    }

    private void notifyPlayerStatusListeners(PlayerVo player, boolean isOnline) {
        for (PlayerStatusListener listener : playerStatusListeners) {
            if (isOnline) {
                listener.onPlayerConnected(player); // 玩家上线通知
            } else {
                listener.onPlayerDisconnected(player); // 玩家下线通知
            }
        }
    }
}
